using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;

namespace JMKeyCreator {
  using JMKeyLib;

  public partial class KeyCreator : Form {
    private char[] reservedChars = ("`~!@#$%^&*()_+=\\][{}|;'\":/?.>,<".ToCharArray());

    private string savedFilename = "";
    private string tempFile = "";

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args) {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);

      if (args.Length > 0) {
        KeyCreator frm = new KeyCreator(args[0]);
        Application.Run(frm);
      }
      else {
        Application.Run(new KeyCreator());
      }
    }

    #region "Class Constructors"
    public KeyCreator() {
      InitializeComponent();
      LoadKeyMasks();
      this.Closing += new CancelEventHandler(KeyCreator_Closing);
    }

    public KeyCreator(string openFile) {
      InitializeComponent();
      LoadKeyMasks();
      this.Closing += new CancelEventHandler(KeyCreator_Closing);

      savedFilename = openFile;
      mnuFileSave.Enabled = true;
      openConfig();
    }
    #endregion

    private void LoadKeyMasks() {
      cboKeyformat.Items.Add("#####-#####-#####-#####-#####");
      cboKeyformat.Items.Add("@@##^-~^###~?-#%#");
      cboKeyformat.Items.Add("###?#-#####-?####-JEF##-%%###");
    }

    private void generateKeys(object sender, EventArgs e) {
      KeyGenerator kg;

      try {
        if (cboKeyformat.Text.Length > 0) {
          btnGenerateKeys.Enabled = false;

          isValidCustomSets();

          keyDisplay.Text = "";
          statusProgBar.Maximum = (int)nudNumKeys.Value;
          statusProgBar.Value = 0;
          statusProgBar.Step = 1;

          AlphabetOnlyFilter aof;
          AlphaNumericFilter anf;

          if (rbANBoth.Checked) { anf = AlphaNumericFilter.AnyCase; }
          else if (rbANLowerOnly.Checked) { anf = AlphaNumericFilter.LowercaseOnly; }
          else { anf = AlphaNumericFilter.UppercaseOnly; }

          if (rbAOBoth.Checked) { aof = AlphabetOnlyFilter.AnyCase; }
          else if (rbAOLower.Checked) { aof = AlphabetOnlyFilter.LowercaseOnly; }
          else { aof = AlphabetOnlyFilter.UppercaseOnly; }

          lblStatus.Text = "Preparing Key Formats ";
          Application.DoEvents();

          kg = new KeyGenerator(anf, aof);
          kg.KeyTemplateMask = cboKeyformat.Text;
          kg.CustomFieldSet1 = txtFirstOcc.Text;
          kg.CustomFieldSet2 = txtSecondOcc.Text;
          kg.CustomFieldSet3 = txtThirdOcc.Text;
          kg.CustomFieldSet4 = txtFourthOcc.Text;
          kg.CustomFieldSet5 = txtFifthOcc.Text;
          kg.CustomFieldSet6 = txtSixthOcc.Text;

          for (int i = 1; i <= (int)nudNumKeys.Value; i++) {
            kg.GenerateKey();
            statusProgBar.Value++;
            Application.DoEvents();
          }

          lblStatus.Text = "Generating Keys ";
          statusProgBar.Value = 0;
          Application.DoEvents();

          foreach (DataRow dr in kg.GetKeySets().Rows) {
            keyDisplay.AppendText(dr[0].ToString() + "\r\n");
            statusProgBar.Value++;
            Application.DoEvents();
          }

          statusProgBar.Value = 0;
          lblStatus.Text = "Key Generation Complete";

          tempFile = Guid.NewGuid().ToString() + ".tmp";
          try {
            kg.GetKeySets().WriteXml(Application.StartupPath + "\\temp\\" + tempFile, XmlWriteMode.WriteSchema);
          }
          catch (System.IO.DirectoryNotFoundException) {
            System.IO.Directory.CreateDirectory(Application.StartupPath + "\\temp");
            kg.GetKeySets().WriteXml(Application.StartupPath + "\\temp\\" + tempFile, XmlWriteMode.WriteSchema);
          }
          finally {
            kg.Dispose();
            kg = null;
            this.mnuFileExport.Enabled = true;
          }
        }
        else {
          MessageBox.Show("Key Template Mask cannot be left blank.", " Key Creator Exception", MessageBoxButtons.OK, MessageBoxIcon.Warning);
          cboKeyformat.Focus();
        }
      }
      catch (Exception ex) {
        MessageBox.Show(ex.Message + ex.StackTrace, " Key Creator Exception", MessageBoxButtons.OK, MessageBoxIcon.Warning);
      }
      finally {
        btnGenerateKeys.Enabled = true;
        this.Focus();
      }
    }

    private void KeyCreator_Closing(object sender, CancelEventArgs e) {
      try {
        string[] tempFiles = System.IO.Directory.GetFiles(Application.StartupPath + "\\temp", "*.tmp");

        for (int i = 0; i < tempFiles.Length; i++) {
          System.IO.File.Delete(tempFiles[i]);
        }
      }
      catch {
        if (!System.IO.Directory.Exists(Application.StartupPath + "\\temp")) {
        }
      }
    }

    #region "Checking Methods"
    private void isValidCustomSets() {
      foreach (Control ctrl in this.tbpCustomRange.Controls) {
        if (ctrl.GetType().ToString() == "System.Windows.Forms.TextBox") {
          if (!isValidString((TextBox)ctrl)) { throw new Exception("Reserved character was found.\r\nPlease remove it from the Custom Range Fields area."); }
        }
      }
    }
    private bool isValidString(TextBox txtBox) {
      bool isValid = true;

      if (txtBox.Text.Length > 0) {
        char[] txt = txtBox.Text.ToCharArray();

        for (int i = 0; i < txtBox.Text.Length; i++) {
          if (isReserved(txt[i])) { isValid = false; break; }
        }
      }
      return isValid;
    }
    private bool isReserved(char character) {
      bool isReservedChar = false;

      for (int i = 0; i < reservedChars.Length; i++) {
        if (reservedChars[i] == character) { isReservedChar = true; break; }
      }
      return isReservedChar;
    }
    #endregion

    #region "Menu Item Methods"
    private void mnuFileOpen_Click(object sender, EventArgs e) {
      OpenFileDialog ofd = new OpenFileDialog();
      ofd.DefaultExt = ".kfg";
      ofd.Filter = "Key Config Files (*.kfg)|*.kfg";
      ofd.InitialDirectory = Application.StartupPath + "\\data";

      if (ofd.ShowDialog() == DialogResult.OK) {
        savedFilename = ofd.FileName;
        mnuFileSave.Enabled = true;
        openConfig();
      }
    }

    private void mnuFileSave_Click(object sender, EventArgs e) {
      if (savedFilename.Length > 0) {
        saveConfig();
      }
      else {
        SaveFileDialog sfd = new SaveFileDialog();
        sfd.DefaultExt = ".kfg";
        sfd.Filter = "Key Config Files (*.kfg)|*.kfg";
        sfd.InitialDirectory = Application.StartupPath + "\\data";

        if (sfd.ShowDialog() == DialogResult.OK) {
          savedFilename = sfd.FileName;
          mnuFileSave.Enabled = true;
          saveConfig();
        }
        sfd.Dispose();
      }
    }

    private void mnuFileSaveAs_Click(object sender, EventArgs e) {
      SaveFileDialog sfd = new SaveFileDialog();
      sfd.DefaultExt = ".kfg";
      sfd.Filter = "Key Config Files (*.kfg)|*.kfg";
      sfd.InitialDirectory = Application.StartupPath + "\\data";

      if (sfd.ShowDialog() == DialogResult.OK) {
        savedFilename = sfd.FileName;
        mnuFileSave.Enabled = true;
        saveConfig();
      }
      sfd.Dispose();
    }

    private void mnuFileExit_Click(object sender, EventArgs e) {
      this.Close();
      Application.Exit();
    }

    private void mnuExportXML_Click(object sender, EventArgs e) {
      SaveFileDialog sfd = new SaveFileDialog();
      sfd.DefaultExt = ".xml";
      sfd.Filter = "XML Key Files (*.xml)|*.xml";
      sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);

      if (sfd.ShowDialog() == DialogResult.OK) { exportToXML(sfd.FileName); }

      sfd.Dispose();
    }

    private void mnuExportText_Click(object sender, EventArgs e) {
      SaveFileDialog sfd = new SaveFileDialog();
      sfd.DefaultExt = ".txt";
      sfd.Filter = "Text Key Files (*.txt)|*.txt";
      sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);

      if (sfd.ShowDialog() == DialogResult.OK) { exportToTxt(sfd.FileName); }

      sfd.Dispose();
    }

    private void mnuExportExcel_Click(object sender, EventArgs e) {
      SaveFileDialog sfd = new SaveFileDialog();
      sfd.DefaultExt = ".csv";
      sfd.Filter = "Excel CSV File (*.csv)|*.csv";
      sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);

      if (sfd.ShowDialog() == DialogResult.OK) { exportToCSV(sfd.FileName, true); }

      sfd.Dispose();
    }

    private void aboutToolStripMenuItem_Click(object sender, EventArgs e) {
      KeyCreatorAbout kca = new KeyCreatorAbout();
      kca.ShowDialog();
    }
    #endregion

    #region "Menu Helping Routines"
    private void saveConfig() {

      DataTable config = new DataTable("KeyCreatorConfig");
      config.Columns.Add("DefaultMask", Type.GetType("System.String"));
      config.Columns.Add("AlphaNum", Type.GetType("System.String"));
      config.Columns.Add("AlphaOnly", Type.GetType("System.String"));
      config.Columns.Add("KeyNumber", Type.GetType("System.Int32"));
      config.Columns.Add("CustomF1", Type.GetType("System.String"));
      config.Columns.Add("CustomF2", Type.GetType("System.String"));
      config.Columns.Add("CustomF3", Type.GetType("System.String"));
      config.Columns.Add("CustomF4", Type.GetType("System.String"));
      config.Columns.Add("CustomF5", Type.GetType("System.String"));
      config.Columns.Add("CustomF6", Type.GetType("System.String"));

      string alphaNumOptions = "";
      if (rbANBoth.Checked) { alphaNumOptions = "both"; }
      else if (rbANLowerOnly.Checked) { alphaNumOptions = "lower"; }
      else { alphaNumOptions = "upper"; }

      string AlphaOnlyOptions = "";
      if (rbAOBoth.Checked) { AlphaOnlyOptions = "both"; }
      else if (rbAOLower.Checked) { AlphaOnlyOptions = "lower"; }
      else { AlphaOnlyOptions = "upper"; }

      DataRow dr = config.NewRow();
      dr[0] = cboKeyformat.Text;
      dr[1] = alphaNumOptions;
      dr[2] = AlphaOnlyOptions;
      dr[3] = nudNumKeys.Value;
      dr[4] = txtFirstOcc.Text;
      dr[5] = txtSecondOcc.Text;
      dr[6] = txtThirdOcc.Text;
      dr[7] = txtFourthOcc.Text;
      dr[8] = txtFifthOcc.Text;
      dr[9] = txtSixthOcc.Text;

      config.Rows.Add(dr);
      config.WriteXml(savedFilename, XmlWriteMode.IgnoreSchema);

      config.Dispose();
    }

    private void openConfig() {
      DataSet config = new DataSet();
      config.ReadXml(savedFilename, XmlReadMode.InferSchema);

      DataRow dr = config.Tables[0].Rows[0];

      cboKeyformat.Text = dr["DefaultMask"].ToString();
      nudNumKeys.Value = int.Parse(dr["KeyNumber"].ToString());

      if (dr["AlphaNum"].ToString() == "both") { rbANBoth.Checked = true; }
      else if (dr["AlphaNum"].ToString() == "lower") { rbANLowerOnly.Checked = true; }
      else { rbANUpperOnly.Checked = true; }

      if (dr["AlphaOnly"].ToString() == "both") { rbAOBoth.Checked = true; }
      else if (dr["AlphaOnly"].ToString() == "lower") { rbAOLower.Checked = true; }
      else { rbAOUpper.Checked = true; }

      txtFirstOcc.Text = dr["CustomF1"].ToString();
      txtSecondOcc.Text = dr["CustomF2"].ToString();
      txtThirdOcc.Text = dr["CustomF3"].ToString();
      txtFourthOcc.Text = dr["CustomF4"].ToString();
      txtFifthOcc.Text = dr["CustomF5"].ToString();
      txtSixthOcc.Text = dr["CustomF6"].ToString();
      config.Dispose();
    }

    private void exportToXML(string Destination) {
      try {
        System.IO.File.Copy(Application.StartupPath + "\\temp\\" + tempFile, Destination);
      }
      catch {
        if (MessageBox.Show("Key Creator was unable to save the file. Would you like to try again?", " JM Key Creator", MessageBoxButtons.RetryCancel, MessageBoxIcon.Information) == DialogResult.Retry) {
          exportToXML(Destination);
        }
      }
    }

    private void exportToTxt(string Destination) {
      System.IO.StreamWriter sw = new System.IO.StreamWriter(Destination, false, Encoding.UTF8);
      try {
        sw.Write(this.keyDisplay.Text);
      }
      catch {
        if (MessageBox.Show("Key Creator was unable to save the file. Would you like to try again?", " JM Key Creator", MessageBoxButtons.RetryCancel, MessageBoxIcon.Information) == DialogResult.Retry) {
          exportToTxt(Destination);
        }
      }
      finally {
        sw.Close();
        sw.Dispose();
      }
    }

    private void exportToCSV(string Destination, bool WriteHeader) {
      System.IO.StreamWriter sw = new System.IO.StreamWriter(Destination, false, Encoding.UTF8);
      DataTable dt = new DataTable();

      try {
        dt.ReadXml(Application.StartupPath + "\\temp\\" + tempFile);

        if (WriteHeader) {
          string[] arr = new String[dt.Columns.Count];
          for (int i = 0; i < dt.Columns.Count; i++) {
            arr[i] = dt.Columns[i].ColumnName;
            arr[i] = getWriteableValue(arr[i]);
          }
          sw.WriteLine(string.Join(",", arr));
        }

        for (int j = 0; j < dt.Rows.Count; j++) {
          string[] dataArr = new String[dt.Columns.Count];
          for (int i = 0; i < dt.Columns.Count; i++) {
            object obj = dt.Rows[j][i];
            dataArr[i] = getWriteableValue(obj);
          }
          sw.WriteLine(string.Join(",", dataArr));
        }
      }
      catch {
        if (MessageBox.Show("Key Creator was unable to save the file. Would you like to try again?", " JM Key Creator", MessageBoxButtons.RetryCancel, MessageBoxIcon.Information) == DialogResult.Retry) {
          exportToCSV(Destination, true);
        }
      }
      finally {
        sw.Close();
        sw.Dispose();
        dt.Dispose();
      }
    }

    private string getWriteableValue(object obj) {
      if (obj == null || obj == Convert.DBNull) {
        return "";
      }
      else if (obj.ToString().IndexOf(",") == -1) {
        return obj.ToString();
      }
      else {
        return "\"" + obj.ToString() + "\"";
      }
    }
    #endregion
  }
}